const isWindows =
  typeof process !== 'undefined' && typeof document === 'undefined' ? process?.platform === 'win32' : false

// Alphabet chars.
const CHAR_UPPERCASE_A = 65 /* A */
const CHAR_LOWERCASE_A = 97 /* a */
const CHAR_UPPERCASE_Z = 90 /* Z */
const CHAR_LOWERCASE_Z = 122 /* z */

// Non-alphabetic chars.
const CHAR_FORWARD_SLASH = 47 /* / */
const CHAR_BACKWARD_SLASH = 92 /* \ */
const CHAR_COLON = 58 /* = */

function setErrorCode(error: any, code: string) {
  error.code = code
  return error
}

function isURLInstance(fileURLOrPath: URL) {
  return fileURLOrPath != null && fileURLOrPath.href && fileURLOrPath.origin
}

function isPathSeparator(code: number) {
  return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH
}

function isWindowsDeviceRoot(code: number) {
  return (
    (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z)
  )
}

function getPathFromURLWin32(url: URL) {
  if (url.protocol !== 'file:')
    throw setErrorCode(new TypeError(`The URL must be of scheme file.`), 'ERR_INVALID_URL_SCHEME')

  let pathname = url.pathname
  for (let n = 0; n < pathname.length; n++) {
    if (pathname[n] === '%') {
      const third = pathname.codePointAt(n + 2)! | 0x20
      if (
        (pathname[n + 1] === '2' && third === 102) || // 2f 2F /
        (pathname[n + 1] === '5' && third === 99)
      ) {
        // 5c 5C \
        throw setErrorCode(
          new TypeError('File URL path must not include encoded \\ or / characters'),
          'ERR_INVALID_FILE_URL_PATH'
        )
      }
    }
  }
  pathname = pathname.replace(/\//g, '\\')
  pathname = decodeURIComponent(pathname)

  // Otherwise, it's a local path that requires a drive letter
  const letter = pathname.codePointAt(1)! | 0x20
  const sep = pathname[2]
  if (
    letter < CHAR_LOWERCASE_A ||
    letter > CHAR_LOWERCASE_Z || // a..z A..Z
    sep !== ':'
  ) {
    throw setErrorCode(new TypeError('File URL path must be absolute'), 'ERR_INVALID_FILE_URL_PATH')
  }
  return pathname.slice(1)
}

function getPathFromURLPosix(url: URL) {
  const pathname = url.pathname
  for (let n = 0; n < pathname.length; n++) {
    if (pathname[n] === '%') {
      const third = pathname.codePointAt(n + 2)! | 0x20
      if (pathname[n + 1] === '2' && third === 102) {
        throw setErrorCode(
          new TypeError('File URL path must not include encoded / characters'),
          'ERR_INVALID_FILE_URL_PATH'
        )
      }
    }
  }
  return decodeURIComponent(pathname)
}

export function fileURLToPath(path: string | URL) {
  if (typeof path === 'string') {
    if (path.startsWith('file:')) {
      path = new URL(path)
    } else {
      return path
    }
  } else if (!isURLInstance(path))
    throw setErrorCode(
      new TypeError(`The "path" argument must be of type string or an instance of URL.`),
      'ERR_INVALID_ARG_TYPE'
    )

  if (path.protocol !== 'file:') {
    return path.href
  }

  return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path)
}

export const dirname = isWindows
  ? function dirname(path: string) {
      const len = path.length
      if (len === 0) return '.'
      let rootEnd = -1
      let offset = 0
      const code = path.charCodeAt(0)

      if (len === 1) {
        // `path` contains just a path separator, exit early to avoid
        // unnecessary work or a dot.
        return isPathSeparator(code) ? path : '.'
      }

      // Try to match a root
      if (isPathSeparator(code)) {
        // Possible UNC root

        rootEnd = offset = 1

        if (isPathSeparator(path.charCodeAt(1))) {
          // Matched double path separator at beginning
          let j = 2
          let last = j
          // Match 1 or more non-path separators
          while (j < len && !isPathSeparator(path.charCodeAt(j))) {
            j++
          }
          if (j < len && j !== last) {
            // Matched!
            last = j
            // Match 1 or more path separators
            while (j < len && isPathSeparator(path.charCodeAt(j))) {
              j++
            }
            if (j < len && j !== last) {
              // Matched!
              last = j
              // Match 1 or more non-path separators
              while (j < len && !isPathSeparator(path.charCodeAt(j))) {
                j++
              }
              if (j === len) {
                // We matched a UNC root only
                return path
              }
              if (j !== last) {
                // We matched a UNC root with leftovers

                // Offset by 1 to include the separator after the UNC root to
                // treat it as a "normal root" on top of a (UNC) root
                rootEnd = offset = j + 1
              }
            }
          }
        }
        // Possible device root
      } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
        rootEnd = len > 2 && isPathSeparator(path.charCodeAt(2)) ? 3 : 2
        offset = rootEnd
      }

      let end = -1
      let matchedSlash = true
      for (let i = len - 1; i >= offset; --i) {
        if (isPathSeparator(path.charCodeAt(i))) {
          if (!matchedSlash) {
            end = i
            break
          }
        } else {
          // We saw the first non-path separator
          matchedSlash = false
        }
      }

      if (end === -1) {
        if (rootEnd === -1) return '.'

        end = rootEnd
      }
      return path.slice(0, end)
    }
  : function dirname(path: string) {
      if (path.length === 0) return '.'
      const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH
      let end = -1
      let matchedSlash = true
      for (let i = path.length - 1; i >= 1; --i) {
        if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
          if (!matchedSlash) {
            end = i
            break
          }
        } else {
          // We saw the first non-path separator
          matchedSlash = false
        }
      }

      if (end === -1) return hasRoot ? '/' : '.'
      if (hasRoot && end === 1) return '//'
      return path.slice(0, end)
    }

export const __esm_default = (mod: any) => {
  return mod && typeof mod === 'object' && 'default' in mod ? mod.default : mod
}
